import authkit.authenticate

from webob import Request, Response, exc
from webinfo import webinfo
from formencode.api import Invalid
from formencode.variabledecode import variable_decode

from controller_lookup import lookup_controller

__all__ = ['ClutchWrapper', 'ControllerLookup', 'SATransaction',
           'Identity', 'Validation', 'ControllerDispatch']

class WSGIApp(object):
    def __init__(self, app=None):
        self.app = app
    def __call__(self, environ, start_response):
        req = webinfo.request = Request(environ)
        try:
            resp = self.pre_hook(req)
        except exc.HTTPRedirection, err:
            resp = err
        if resp is None:
            resp = req.get_response(self.app)
        hook_resp = self.post_hook(req, resp)
        if hook_resp is not None:
            resp = hook_resp
        result = resp(environ, start_response)
        return result
    def pre_hook(self, request): pass
    def post_hook(self, request, response): pass

class TransactionMiddleware(object):
    def __init__(self, app):
        self.app = app
    def __call__(self, environ, start_response):
        req = webinfo.request
        controller = req.environ['clutch.controller']
        transaction_type = controller.tg_info.transaction_type
        state = None
        # Handle when there are no transactions
        if transaction_type is None:
            resp = req.get_response(self.app)
            result = resp(environ, start_response)
            return result
        try:
            state = self.begin(transaction_type)
            try:
                resp = req.get_response(self.app)
                result = resp(environ, start_response)
                self.commit(state)
                return result
            except exc.HTTPRedirection:
                self.commit(state)
                raise
            except:
                self.rollback(state)
                raise
        finally:
            self.close(state)
    def begin(self, transaction_type):
        '''Return state which is used in commit, rollback, and close'''
    def commit(self, state):
        '''Commit the transaction'''
    def rollback(self, state):
        '''Rollback the transaction'''
    def close(self, state):
        '''Cleanup any request-based resources'''

class ClutchWrapper(WSGIApp):

##    def __call__(self, environ, start_response):
##        req = webinfo.request = Request(environ)
##        return WSGIApp.__call__(self, environ, start_response)

    def pre_hook(self, req):
        params = variable_decode(req.params.mixed())
        url_path = req.path_info.split('/')[1:]
        req.environ['clutch.params'] = params
        req.environ['clutch.controller'] = None
        req.environ['clutch.url_path'] = url_path

class ControllerLookup(WSGIApp):

    def __init__(self, app, root_controller):
        WSGIApp.__init__(self, app)
        self.root_controller = root_controller

    def pre_hook(self, req):
        url_path = req.environ['clutch.url_path']
        try:
            controller, remainder = lookup_controller(self.root_controller, url_path)
            req.environ['clutch.controller'] = controller
            req.environ['clutch.remainder'] = remainder
        except exc.HTTPRedirection, err:
            return err
        except exc.HTTPNotFound, err:
            return err

class SATransaction(TransactionMiddleware):
    def begin(self, transaction_type):
        from database import session
        return session()
    def commit(self, session):
        session.commit()
    def rollback(self, session):
        session.rollback()
    def close(self, state):
        from database import session
        session.remove()

class Identity(WSGIApp):
    def dummy_app(self, environ, start_response):
        pass
    def dummy_start_response(self, *l, **kw):
        pass
    def pre_hook(self, req):
        permissions = req.environ['clutch.controller'].tg_info.security_descriptors
        for p in permissions:
            print 'Checking %s...' % p
            p.check(self.dummy_app, req.environ, self.dummy_start_response)
            print 'pass!'
        
class Validation(WSGIApp):

    def pre_hook(self, req):
        controller = req.environ['clutch.controller']
        remainder = req.environ['clutch.remainder']
        validator = controller.tg_info.validator
        if validator is None: return
        validator.allow_extra_fields = True
        validator.if_key_missing = None
        error_handler = controller.tg_info.error_handler
        try:
            params = req.environ['clutch.params']
            req.environ['clutch.original_params'] = params
            new_params = validator.to_python(params)
            req.environ['clutch.params'] = new_params
        except Invalid, inv:
            if error_handler is None:
                error_handler = controller
            req.environ['clutch.params'].update(tg_errors=inv.dict())
            req.environ['clutch.controller'] = error_handler

class ControllerDispatch(WSGIApp):

    def __init__(self):
        from pylons.templating import Buffet
        self._buffet = Buffet()
        self._configured_buffet_engines = set()

    def pre_hook(self, req):
        resp = webinfo.response = Response()
        controller = req.environ['clutch.controller']
        remainder = req.environ['clutch.remainder']
        params = req.environ['clutch.params']
        print 'Dispatch to %s' % controller.__name__
        body = controller(*remainder, **params)
        resp.body = self._render_response_body(body, req, controller)
        return resp

    def _render_response_body(self, body, req, controller):
        content_type, engine_name, template_name, exclude_names = \
                      controller.tg_info.lookup_template_engine(req)
        if engine_name is None: return body
        if not self._buffet.engines.get(engine_name):
            self._buffet.prepare(engine_name)
        namespace = dict(body)
        for name in exclude_names:
            namespace.pop(name, None)
        result = self._buffet.render(engine_name=engine_name,
                                     template_name=template_name,
                                     include_pylons_variables=False,
                                     namespace=namespace)
        webinfo.response.headers['Content-Type'] = content_type
        return result

